﻿using System.Collections;
using System.Collections;
using System.Collections.Generic;
using SLua;
using UnityEngine;

public class LuaManager : ModuleBase<LuaManager>
{
    LuaSvr m_lua;                   // Lua服务
    System.Action m_onStartFinish;  // 启动完成回调

    Dictionary<string, LuaFunction> m_functions = new Dictionary<string, LuaFunction>();
    Dictionary<string, byte[]> m_caches = new Dictionary<string, byte[]>();

    AssetBundle m_abLua;
    AssetBundle m_abLuaPatch;

    public LuaManager()
    {
        Framework.Instance.AddUpdate(this);
        Framework.Instance.AddFixedUpdate(this);
        Framework.Instance.AddEventProc(EventID.START_LUA, OnStart);

        SLua.Logger.LogAction = LuaLogAction;
    }

    void LuaLogAction(SLua.Logger.Level level, string msg)
    {
        switch (level)
        {
            case SLua.Logger.Level.Debug:
                Debuger.Log(msg);
                break;
            case SLua.Logger.Level.Warning:
                Debuger.LogWarning(msg);
                break;
            case SLua.Logger.Level.Error:
                try
                {
                    Debuger.LogError(msg);
                }
                catch (System.Exception ex)
                {
                    Debug.LogError(msg);
                }
                break;
        }
    }

    void OnStart(EventBase evt)
    {
        Clear();

        m_onStartFinish = (evt as CallbackEvent).OnFinish;

        // 启动Lua虚拟机
        SDKReportHelper.DoReport(ReportType.StartLuaState);

        m_lua = new LuaSvr();
        m_lua.init(OnProgress, OnCompleted, LuaSvrFlag.LSF_EXTLIB | LuaSvrFlag.LSF_3RDDLL);
    }

    void OnProgress(int progress)
    {

    }

    void OnCompleted()
    {
        LuaState.main.loaderDelegate = Loader;
        if (Config.Instance.UseAssetBundle)
            LoadPackage();
        else
            StartScript();
    }

    void LoadPackage()
    {
        // 加载Lua包
        SDKReportHelper.DoReport(ReportType.LoadLuaPackage);

        // 是否有Lua补丁
        string patchFullPath = LoadManager.Instance.SearchFilePath(FileHelper.ConvertArchName(Config.LuaArch, Config.LUA_PATCH), true, true);
        bool hasPatch = !string.IsNullOrEmpty(patchFullPath);

        // 原包
        LoadPackage(false, !hasPatch);
        if (hasPatch)
        {
            // 有补丁,加载补丁
            LoadPackage(true, true);
        }
        else if (!string.Equals(Config.VersionBits[2], "001"))
        {
            string logString = string.Format("No Found Lua Patch, version={0}", Config.Version);
            // 正常情况下,有更新一定会有lua补丁,如果没有,上报一条日志
            if (Config.Release == ReleaseType.RELEASE)
            {
                string stackTrace = "";
                ErrorManager.Instance.ErrorLog(logString, stackTrace, LogType.Error, ErrorManager.ErrorType.Client_Error);
            }
            else
            {
                Debug.LogError(logString);
            }
            //重置资源版本号
            var ver = Config.VersionBits;
            ver[2] = "001";
            string strVer = "";
            for (int i = 0; i < ver.Length; i++)
            {
                if (i == ver.Length - 1)
                    strVer += ver[i];
                else
                    strVer += ver[i] + ".";
            }
            Config.Version = strVer;
            FileHelper.DeleteFile(string.Format("{0}/version", Config.UPDATE_DATA_PATH));
        }
    }

    void LoadPackage(bool patch, bool start)
    {
        string name = GetLuaPackerName(patch);
        LoadManager.Instance.LoadBundle(name, (data) =>
        {
            AssetBundleCache cache = data as AssetBundleCache;
            if (cache == null)
            {
                Debuger.LogError("Lua包cache加载失败！name:{0}", name);
                ErrorManager.Instance.ErrorLog(string.Format("Lua包cache加载失败！name:{0}", name), "", LogType.Error, ErrorManager.ErrorType.Client_Error);
                return;
            }

            AssetBundle ab = cache.Bundle;
            if (ab == null)
            {
                Debuger.LogError("Lua包加载失败！name:{0}", name);
                ErrorManager.Instance.ErrorLog(string.Format("Lua包加载失败d！name:{0}", name), "", LogType.Error, ErrorManager.ErrorType.Client_Error);
                return;
            }

            if (Config.LUA_DATA)
            {
                LuaData asset = ab.LoadAsset<LuaData>(Config.LUA_PACKAGE);
                if (asset == null)
                {
                    Debuger.LogError("Lua资源加载失败");
                    ErrorManager.Instance.ErrorLog(string.Format("Lua资源加载失败！name:{0}", name), "", LogType.Error, ErrorManager.ErrorType.Client_Error);
                }
                else
                {
                    RegisterCache(asset);
                    Resources.UnloadAsset(asset);
                }
            }
            else
            {
                if (patch)
                    m_abLuaPatch = ab;
                else
                    m_abLua = ab;
            }
            if (start)
                StartScript();
        }, true, false, false);
    }

    string GetLuaPackerName(bool patch)
    {
        return FileHelper.ConvertArchName(Config.LuaArch, patch ? Config.LUA_PATCH : Config.LUA_PACKAGE);
    }

    public void RegisterCache(string name, byte[] bytes)
    {
        if (!m_caches.ContainsKey(name))
            m_caches.Add(name, bytes);
        else
            m_caches[name] = bytes;
    }

    public void RegisterCache(LuaData data)
    {
        LuaData.FileInfo info;
        byte[] bytes;

        for (int i = 0; i < data.files.Count; ++i)
        {
            info = data.files[i];
            bytes = info.bytes;
            switch (data.encode)
            {
                case LuaData.EncodeType.Base64:
                    bytes = System.Convert.FromBase64String(System.Text.Encoding.Default.GetString(info.bytes));
                    break;
            }
            RegisterCache(info.name, bytes);
        }
    }

    public void RegisterCache(AssetBundle ab)
    {
        if (ab)
        {
            LuaData asset = ab.LoadAsset<LuaData>(Config.LUA_PACKAGE);
            if (asset == null)
                Debuger.LogError("Lua资源加载失败");
            else
            {
                RegisterCache(asset);
                Resources.UnloadAsset(asset);
            }
        }
    }

    void StartScript()
    {
        // 启动Lua主程序
        SDKReportHelper.DoReport(ReportType.StartLuaMain);
        m_lua.start(Config.LUA_MAIN);
        if (m_onStartFinish != null)
        {
            m_onStartFinish();
            m_onStartFinish = null;
        }
    }

    public override void Update(float dt)
    {
        CallFunc(LuaEventDefine.UPDATE, dt);
    }

    public override void FixedUpdate(float dt)
    {
        CallFunc(LuaEventDefine.FIXED_UPDATE, dt);
    }

    public void RegisterFunc(string name, LuaFunction func)
    {
        if (!m_functions.ContainsKey(name))
            m_functions.Add(name, func);
        else
            m_functions[name] = func;
    }

    public void UnregisterFunc(string name)
    {
        if (m_functions.ContainsKey(name))
        {
            m_functions.Remove(name);
        }
    }

    public void Clear()
    {
        if (m_lua != null)
        {
            CallFunc(LuaEventDefine.SHUT_DOWN);

            LuaSvr.mainState.Dispose();
            m_lua = null;
        }

        m_functions.Clear();

        if (Config.LUA_DATA)
        {
            m_caches.Clear();
        }
        else
        {
            m_abLua = null;
            m_abLuaPatch = null;

            LoadManager.Instance.UnloadBundle(GetLuaPackerName(false), true);
            LoadManager.Instance.UnloadBundle(GetLuaPackerName(true), true);
        }
    }

    public override void Dispose()
    {
        Clear();
        base.Dispose();
    }

    #region Recall Lua Function
    private LuaFunction GetFunction(string name)
    {
        LuaFunction func;
        m_functions.TryGetValue(name, out func);
        return func;
    }
    public object CallFunc(string name)
    {
        var func = GetFunction(name);
        if (func != null)
            return func.call();
        return null;
    }
    public object CallFunc(string name, object arg1)
    {
        var func = GetFunction(name);
        if (func != null)
            return func.call(arg1);
        return null;
    }
    public object CallFunc(string name, object arg1, object arg2)
    {
        var func = GetFunction(name);
        if (func != null)
            return func.call(arg1, arg2);
        return null;
    }
    public object CallFunc(string name, object arg1, object arg2, object arg3)
    {
        var func = GetFunction(name);
        if (func != null)
            return func.call(arg1, arg2, arg3);
        return null;
    }
    #endregion

    static byte[] Loader(string name, ref string absoluteFn)
    {
        byte[] ret = null;
        LuaManager mgr = Instance;
        if (Config.Instance.UseAssetBundle)
        {
            string tempName = name.Replace('.', '_').Replace('/', '_');
            if (Config.LUA_DATA)
            {
                if (mgr.m_caches.ContainsKey(tempName))
                    ret = mgr.m_caches[tempName];
            }
            else
            {
                TextAsset asset = null;
                if (mgr.m_abLuaPatch && mgr.m_abLuaPatch.Contains(tempName))
                    asset = mgr.m_abLuaPatch.LoadAsset<TextAsset>(tempName);
                else if (mgr.m_abLua && mgr.m_abLua.Contains(tempName))
                    asset = mgr.m_abLua.LoadAsset<TextAsset>(tempName);
                if (asset != null)
                    ret = asset.bytes;
            }
        }
#if UNITY_EDITOR || UNITY_STANDALONE
        if (ret == null)
            ret = LoadLuaFile(name);
#endif
        return ret;
    }

    static byte[] LoadLuaFile(string name)
    {
        byte[] ret = null;
        string tempName = name.Replace('.', '/');
        string path = string.Format("{0}/{1}.lua", Config.LUA_PATH, tempName);
        //Debuger.Log("load lua>>" + path);
        LoadManager.Instance.LoadFile(path, (data) =>
        {
            ret = data as byte[];
        }, false, false);

        return ret;
    }
}